package com.zombie.state;

import static com.zombie.Constants.GROUP_LAST;
import static com.zombie.Constants.GROUP_NORMAL;
import static com.zombie.Constants.GROUP_POST_EFFECT;
import static com.zombie.Constants.GROUP_POST_NORMAL;
import static com.zombie.Constants.GROUP_PRE_EFFECT;
import static com.zombie.Constants.GROUP_PRE_NORMAL;
import static com.zombie.Constants.MAP_HEIGHT;
import static com.zombie.Constants.MAP_WIDTH;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.ParticleEmitter;
import com.badlogic.gdx.graphics.g2d.ParticleEmitterBox2DScalable;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Box2DDebugRendererScaled;
import com.zombie.Constants;
import com.zombie.Renderable;
import com.zombie.effect.FogEffect;
import com.zombie.input.Input;
import com.zombie.logic.core.ZombieWorld;
import com.zombie.logic.level.Level;
import com.zombie.logic.level.wave.Spawn;
import com.zombie.logic.level.wave.Wave;
import com.zombie.logic.level.wave.WaveStarter;
import com.zombie.logic.object.Player;
import com.zombie.util.ThreadPoolManager;
import com.zombie.util.Utils;
import com.zombie.util.state.BasicGameState;
import com.zombie.util.state.State;

public class GameState extends BasicGameState {

	private static GameState instance = new GameState();
	public BitmapFont font;
	public Player player;
	private SpriteBatch batch;
	private ShapeRenderer shapeBatch;
	private Box2DDebugRendererScaled boxrender = new Box2DDebugRendererScaled();
	private OrthographicCamera camera;
	private TileMapRenderer mapRenderer;
	
	
	public static int time;
	public static int zombies;
	public float zoom = 1f;
	ParticleEmitter bomb;
	ParticleEffect effect = new ParticleEffect();
	public float offsetX;
	public float offsetY;
	public long waveStarted;
	public Wave currentWave;
	public boolean waitNewWave;
	
	public static GameState getInstance(){
		return instance;
	}
	
	@Override
	public int getID() {
		return Constants.STATE_GAME;
	}

	@Override
	public void init() {
		batch = new SpriteBatch();
		font = new BitmapFont();
		camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		camera.setToOrtho(false);
		shapeBatch = new ShapeRenderer();
	}

	public void startNewLevel(Level level){
		time = 0;
		camera.position.set(new Vector3());
		if (mapRenderer != null)
			mapRenderer.dispose();
		mapRenderer = new TileMapRenderer(level.tiledMap,level.tileAtlas,
				8,
				8, 32, 32);
		player  = new Player(Constants.START_X, Constants.START_Y);
		ZombieWorld.addObject(player);
		effect.getEmitters().clear();
		effect.loadEmitters(Gdx.files.internal("data/particle5.p"));
		effect.loadEmitterImages(Gdx.files.internal("data"));	
		bomb = new ParticleEmitterBox2DScalable(ZombieWorld.world, effect.getEmitters().get(0));
		effect.getEmitters().removeIndex(0);
		effect.getEmitters().add(bomb);
		currentWave = null;
		ThreadPoolManager.getInstance().scheduleGeneral(new WaveStarter(0), 5000L);
		zombies = 0;
		
		grass = Utils.getLayerIndex("grass",level.tiledMap);
		buildings = Utils.getLayerIndex("buildings",level.tiledMap);
		upper = Utils.getLayerIndex("upperlayer",level.tiledMap);
		
		
		for(int i =0;i < 6;i++){
			FogEffect eff = new FogEffect(1f+2*Utils.rnd.nextFloat());
			eff.position.x = Utils.rnd.nextInt(MAP_WIDTH);
			eff.position.y = Utils.rnd.nextInt(MAP_HEIGHT);
			ZombieWorld.addEffect(eff);
		}
		
//		for(int i = 0 ; i < 50000;i++){
//			float x,y,angle,scale;
//			TextureRegion region;
//			int b = Utils.rnd.nextInt(4);
////			if (b == 0)
////				region= ResourceManager.getImage("box_0");
////			else if (b == 1)
////				region= ResourceManager.getImage("box_1");
////			else if (b == 2)
////				region= ResourceManager.getImage("box_2");
////			else
//				region= ResourceManager.getImage("barrel_1");			
//			
//			x = offsetX-Gdx.graphics.getWidth()/2 + Utils.randomInt(0, Gdx.graphics.getWidth());
//			y = offsetY-Gdx.graphics.getHeight()/2 + Utils.randomInt(0, Gdx.graphics.getHeight());
//			angle = Utils.randomInt(0, 359);
//			scale = 0.1f + 2*Utils.rnd.nextFloat();
//			ImageEffect eff = new ImageEffect();
//			eff.setBounds(x, y, region);
//			eff.angle = angle;
//			eff.scale = 1;
//			eff.setFullLifeTime(1000000000);
//			eff.renderGroup = Constants.GROUP_POST_EFFECT;
//			ZombieWorld.addEffect(eff);
//		}
		
	}
	
	//indexes of tiledmap layers
	int grass,buildings,upper;

	@Override
	public void render() {
		camera.update();
		
		batch.setProjectionMatrix(camera.combined);
		shapeBatch.setProjectionMatrix(camera.combined);

		//background
		for(int i = 0;i < ZombieWorld.level.backgroundCount;i++)
			mapRenderer.render(camera, new int[]{i});
		
		//pre effect 
		batch.begin();
		for(Renderable r : ZombieWorld.renderGroups.get(GROUP_PRE_EFFECT))
			r.draw(batch, shapeBatch);
		batch.end();
		
		//grass
		mapRenderer.render(camera, new int[]{grass});

		//pre_normal
		batch.begin();
		for(Renderable r : ZombieWorld.renderGroups.get(GROUP_PRE_NORMAL))
			r.draw(batch, shapeBatch);
		batch.end();

		// normal - objects	
		batch.begin();
		for(Renderable r : ZombieWorld.renderGroups.get(GROUP_NORMAL))
			r.draw(batch, shapeBatch);
		batch.end();

		// post_normal
		batch.begin();
		for(Renderable r : ZombieWorld.renderGroups.get(GROUP_POST_NORMAL))
			r.draw(batch, shapeBatch);
		batch.end();
		
		//buildings
		mapRenderer.render(camera, new int[]{buildings});

		// post_effects
		batch.begin();
		for(Renderable r : ZombieWorld.renderGroups.get(GROUP_POST_EFFECT))
			r.draw(batch, shapeBatch);
		batch.end();
		
		//trees
		mapRenderer.render(camera, new int[]{upper});
	
		// last
		batch.begin();
		for(Renderable r : ZombieWorld.renderGroups.get(GROUP_LAST))
			r.draw(batch, shapeBatch);
		batch.end();
		if (shapeBatch.getCurrentType() != null)
			shapeBatch.end();

		if (Constants.DEBUG)
			drawDebugData();
	}

	private void drawDebugData() {
		boxrender.render(ZombieWorld.world, camera.combined);
		batch.begin();
		font.draw(batch, "Total objects: " + ZombieWorld.objects.size(),
				offsetX-Gdx.graphics.getWidth()/2, offsetY+Gdx.graphics.getHeight()/2);
		font.draw(batch, "Total effects: " + ZombieWorld.effects.size(),
				offsetX-Gdx.graphics.getWidth()/2, offsetY+Gdx.graphics.getHeight()/2-15);
		batch.end();
	}

	@Override
	public void update(float delta) {
		time+= delta*1000;
		ZombieWorld.update(delta);
		moveCam();		
		updateWave(delta);
	}

	public void updateEffect(int mx,int my){
		effect.reset();
		effect.setPosition(mx+offsetX-Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()-my+offsetY-Gdx.graphics.getHeight()/2);
	}
	
	public void moveCam() {
		if (player == null)
			return;
		float x = offsetX;
		float y = offsetY;
		float x1,y1;
		x -= (x - (player.position.x))/3;
		y -= (y - (player.position.y))/3;
		x1 = (offsetX-(player.position.x));
		y1 = (offsetY-(player.position.y));
		x1 = Math.round(x1);
		y1 = Math.round(y1);
//		float scale = 1f;
//		if (x1 > 2*scale)
//			x1 = 2*scale;
//		if (y1 > 2*scale)
//			y1 = 2*scale;
//		if (x1 < -2*scale)
//			x1 = -2*scale;
//		if (y1 < -2*scale)
//			y1 = -2*scale; 
		offsetX -= x1;
		offsetY -= y1;
		
//		if (offsetX < SCREEN_WIDTH/2)
//			offsetX = SCREEN_WIDTH/2;
//		if (offsetX > MAP_WIDTH-SCREEN_WIDTH/2)
//			offsetX = MAP_WIDTH-SCREEN_WIDTH/2;
//		if (offsetY < SCREEN_HEIGHT/2)
//			offsetY = SCREEN_HEIGHT/2;
//		if (offsetY > MAP_HEIGHT-SCREEN_HEIGHT/2)
//			offsetY = MAP_HEIGHT-SCREEN_HEIGHT/2;
		if (zoom <= 0)
			zoom = 1;
		camera.zoom = zoom;
		camera.position.set(offsetX, offsetY, 0);
	}
	
	
	public void updateWave(float f) {
       	if (currentWave!= null){
       		long t = time-waveStarted;
       		for(Spawn s: currentWave.spawns.values()){
       			if (s.time < t  && !s.spawned)
       				s.spawn(currentWave);
       		}
       		boolean allSpawned = true;
       		for(Spawn s: currentWave.spawns.values())
       			if (!s.spawned){
       				allSpawned = false;
       			}
       		if (allSpawned && !waitNewWave && zombies < 10){
       			if (ZombieWorld.level.waves.containsKey(currentWave.id+1)){
       				ThreadPoolManager.getInstance().scheduleGeneral(new WaveStarter(currentWave.id+1), 10000L);
//	       			TextEffect eff = new TextEffect();
//	       			eff.text = "Next wave starts in 10 sec.";
//	       			eff.position.set(350,-270);
//	       			eff.position.add(offsetX-Gdx.graphics.getWidth()/2, offsetY+Gdx.graphics.getHeight()/2);
//	       			ZombieWorld.addEffect(eff);
	       			waitNewWave = true;
       			}
       		}
       	}
	}
	
	@Override
	public void enter(State from) {
		Input.addInputProcessor(this);
	}

	@Override
	public void leave(State to) {
		Input.removeInputProcessor(this);
	}

	@Override
	public void resize(int width, int height) {
		// TODO Auto-generated method stub
		
	}

}
